/*************************************************************************
 *
 * hodiny realneho casu
 * 
 * (c) Ales Povalac, alpov@alpov.net, http://www.alpov.net/
 * 
 *************************************************************************
 *
 * $Id: rtc.c 126 2007-02-10 14:57:23Z alpov $
 * 
 *************************************************************************/         

#include <stdlib.h>
#include <stdint.h>
#include <avr/interrupt.h>
#include "../avrx/avrx.h"
#include "../avrx/AvrXFifo.h"
#include "rtc.h"
#include "data.h"
#include "i2cmaster.h"
#include "datetime.h"

Mutex RtcMutex;


/*************************************************************************
 * inicializace hodin - hardware
 *************************************************************************/ 
void InitRtc(void)
{
    i2c_init();
    AvrXSetSemaphore(&RtcMutex);
}


/*************************************************************************
 * prevod cislo -> bcd
 *************************************************************************/ 
unsigned char number_to_bcd(unsigned char x)
{
    div_t qr = div(x, 10);
    return (qr.quot << 4) | qr.rem;
}


/*************************************************************************
 * prevod bcd -> cislo
 *************************************************************************/ 
unsigned char bcd_to_number(unsigned char x)
{
    return 10 * (x >> 4) + (x & 0x0f);
}


/*************************************************************************
 * nacteni bloku z i2c ram
 *************************************************************************/ 
void ReadRtcBlock(char *pointer_ram, unsigned char addr, unsigned char n)
{
    AvrXWaitSemaphore(&RtcMutex);
    
    i2c_start_wait(DevPCF8583+I2C_WRITE);
    i2c_write(addr);
    i2c_rep_start(DevPCF8583+I2C_READ);
    for (int i = 0; i < (n - 1); i++) {
        *pointer_ram = i2c_readAck();
        pointer_ram++;        
    }
    *pointer_ram = i2c_readNak();
    i2c_stop();

    AvrXSetSemaphore(&RtcMutex);
}


/*************************************************************************
 * zapis bloku do i2c ram
 *************************************************************************/ 
void WriteRtcBlock(char *pointer_ram, unsigned char addr, unsigned char n)
{
    AvrXWaitSemaphore(&RtcMutex);
    
    i2c_start_wait(DevPCF8583+I2C_WRITE);
    i2c_write(addr);
    for (int i = 0; i < n; i++) {
        i2c_write(*(char*)pointer_ram);
        pointer_ram++;        
    }
    i2c_stop();
    
    AvrXSetSemaphore(&RtcMutex);
}


/*************************************************************************
 * zapis status registru do i2c ram
 *************************************************************************/ 
void WriteRtcStatus(unsigned char status)
{
    WriteRtcBlock(&status, PCF_STATUS, sizeof(status));
}


/*************************************************************************
 * cteni hodin
 *************************************************************************/ 
unsigned long ReadRtc(void)
{
    unsigned char s[5];
    unsigned char year, month, day, hour, min, sec;
    
    WriteRtcStatus(0b01000000); // hold last count flag
    
    ReadRtcBlock(s, PCF_DATETIME, sizeof(s));
    ReadRtcBlock(&year, PCF_YEAR, sizeof(year));
    
    WriteRtcStatus(0b00000000); // normal op
    
    year |= s[3] >> 6;
    month = bcd_to_number(s[4] & 0x1f);
    day = bcd_to_number(s[3] & 0x3f);
    hour = bcd_to_number(s[2] & 0x3f);
    min = bcd_to_number(s[1]);
    sec = bcd_to_number(s[0]);
    
    return encode_datetime(year, month, day, hour, min, sec);
}


/*************************************************************************
 * zapis
 *************************************************************************/ 
void WriteRtc(unsigned long datetime)
{
    unsigned char s[5];
    unsigned char year, month, day, hour, min, sec;
    
    decode_date(datetime, &year, &month, &day);
    decode_time(datetime, &hour, &min, &sec);
    
    s[0] = number_to_bcd(sec);
    s[1] = number_to_bcd(min);
    s[2] = number_to_bcd(hour);
    s[3] = number_to_bcd(day) | ((year & 0x03) << 6);
    s[4] = number_to_bcd(month);
    year &= 0xfc;
    
    WriteRtcStatus(0b10000000); // stop counting flag
    
    WriteRtcBlock(s, PCF_DATETIME, sizeof(s));
    WriteRtcBlock(&year, PCF_YEAR, sizeof(year));
    
    WriteRtcStatus(0b00000000); // normal op
}

